package reflect

import (
	"fmt"
	"reflect"
)

// 变量, reflect.Type(接口), reflect.Value(结构体) 三者可以互相转换

type Human struct {
	Name string `name:"haha"`
	Age  int    `name:"hehe"`
}

func (u Human) Say() string {
	return fmt.Sprintf("My name is %s,I'm %d years old\n", u.Name, u.Age)
}

func (u Human) CulPlus(a int, b int) int {
	return a + b
}

type Integer int

func ReflectDemo() {
	param1 := 5
	reflecttest(param1)
	param2 := Human{"小明", 10}
	reflecttest(param2)
	param3 := Integer(100)
	reflecttest(param3)
}

func reflecttest(param interface{}) {
	//变量转为reflect.Type
	//Type代表的是类型
	paramType := reflect.TypeOf(param)
	fmt.Printf("type为%v,类型为%T\n", paramType, paramType)

	//变量转为reflect.Value
	//Value代表值
	paramValue := reflect.ValueOf(param)
	fmt.Printf("value为%v,类型为%T\n", paramValue, paramValue)

	//将reflect.Value转为空接口 value.Interface()
	//可结合类型断言使用
	paramKind := paramType.Kind()
	fmt.Printf("paramKind为%v,类型为%T,代表的值为%d\n", paramKind, paramKind, paramKind)
}

// 通过结构体获取结构体的字段,方法,以及标签

func GetStructInfo() {
	h := Human{"小明", 20}
	testStructField(h)
	testStructMethod(h)

	//h2 := new(Human)
	//testUpdateFields(h2)
	//fmt.Println(h2)
}

func testStructField(param interface{}) {
	paramType := reflect.TypeOf(param)
	paramValue := reflect.ValueOf(param)
	paramKind := paramValue.Kind()
	if paramKind != reflect.Struct {
		fmt.Printf("不是结构体\n")
		return
	}
	nums := paramValue.NumField()
	fmt.Printf("该结构体共有%d个字段\n", nums)
	for i := 0; i < nums; i++ {
		fmt.Printf("字段%s的值为%v,类型为%v\n", paramType.Field(i).Name, paramValue.Field(i), paramType.Field(i).Type)
		fmt.Printf("字段name标签的值为%v\n", paramType.Field(i).Tag.Get("name"))
	}
}

func testStructMethod(param interface{}) {
	paramType := reflect.TypeOf(param)
	paramValue := reflect.ValueOf(param)

	paramKind := paramValue.Kind()
	if paramKind != reflect.Struct {
		fmt.Printf("不是结构体\n")
		return
	}
	nums := paramType.NumMethod()
	fmt.Printf("该结构体共有%d个方法\n", nums)
	for i := 0; i < nums; i++ {
		fmt.Printf("方法名为%s\n", paramType.Method(i).Name)
	}

	method0 := paramValue.Method(0)
	param1 := reflect.ValueOf(4)
	param2 := reflect.ValueOf(5)
	params := []reflect.Value{param1, param2}
	resValue := method0.Call(params)[0]
	fmt.Printf("result is %d\n", resValue.Int())

	method1 := paramValue.Method(1)
	params2 := []reflect.Value{}
	resValue2 := method1.Call(params2)[0]
	fmt.Printf("result is %s\n", resValue2.String())

	fmt.Printf(paramType.Method(0).Name)
}

func testUpdateFields(param interface{}) {
	paramV := reflect.ValueOf(param)
	paramV.Elem().FieldByName("Name").SetString("小东")
	paramV.Elem().FieldByName("Age").SetInt(20)
}
